fdo#75053: Adjust reference update on shift for formula groups.

This is similar to my earlier fix for reference update on moving of
cells.

Change-Id: I592599507bfcab12f611eeae7b56c99da6c31919
diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index 42e00a9c..f75895b 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -132,14 +132,6 @@ private:
                    };
    void            InterpretTail( ScInterpretTailParameter );

    bool UpdatePosOnShift( const sc::RefUpdateContext& rCxt );

    /**
     * Update reference in response to cell insertion or deletion.
     */
    bool UpdateReferenceOnShift(
        const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos );

    /**
     * Update reference in response to cell copy-n-paste.
     */
@@ -213,6 +205,7 @@ public:
    void ResetDirty();
    bool NeedsListening() const;
    void SetNeedsListening( bool bVar );
    void SetNeedsDirty( bool bVar );
    void SetNeedNumberFormat( bool bVal );
    short GetFormatType() const;
    void            Compile(const OUString& rFormula,
@@ -246,6 +239,19 @@ public:
        const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc = NULL, const ScAddress* pUndoCellPos = NULL );

    /**
     * Shift the position of formula cell as part of reference update.
     *
     * @return true if the position has shifted, false otherwise.
     */
    bool UpdatePosOnShift( const sc::RefUpdateContext& rCxt );

    /**
     * Update reference in response to cell insertion or deletion.
     */
    bool UpdateReferenceOnShift(
        const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos );

    /**
     * Update reference in response to cell move.
     */
    bool UpdateReferenceOnMove(
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index 4b8d38cd9..b7fd479 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -2428,6 +2428,68 @@ class UpdateRefOnNonCopy : std::unary_function<FormulaGroup, void>
    ScDocument* mpUndoDoc;
    bool mbUpdated;

    void updateRefOnShift( FormulaGroup& rGroup )
    {
        if (!rGroup.mbShared)
        {
            ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
            mbUpdated |= rGroup.mpCell->UpdateReferenceOnShift(*mpCxt, mpUndoDoc, &aUndoPos);
            return;
        }

        // Update references of a formula group.
        ScFormulaCell** pp = rGroup.mpCells;
        ScFormulaCell** ppEnd = pp + rGroup.mnLength;
        ScFormulaCell* pTop = *pp;
        ScTokenArray* pCode = pTop->GetCode();
        boost::scoped_ptr<ScTokenArray> pOldCode(pCode->Clone());
        ScAddress aOldPos = pTop->aPos;

        // Run this before the position gets updated.
        sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(*mpCxt, aOldPos);

        if (pTop->UpdatePosOnShift(*mpCxt))
        {
            // Update the positions of all formula cells.
            for (++pp; pp != ppEnd; ++pp) // skip the top cell.
            {
                ScFormulaCell* pFC = *pp;
                pFC->aPos.Move(mpCxt->mnColDelta, mpCxt->mnRowDelta, mpCxt->mnTabDelta);
            }

            if (pCode->IsRecalcModeOnRefMove())
                aRes.mbValueChanged = true;
        }

        if (aRes.mbReferenceModified)
        {
            sc::StartListeningContext aStartCxt(mpCxt->mrDoc);
            sc::EndListeningContext aEndCxt(mpCxt->mrDoc, pOldCode.get());
            aEndCxt.setPositionDelta(
                ScAddress(-mpCxt->mnColDelta, -mpCxt->mnRowDelta, -mpCxt->mnTabDelta));

            for (pp = rGroup.mpCells; pp != ppEnd; ++pp)
            {
                ScFormulaCell* p = *pp;
                p->EndListeningTo(aEndCxt);
                p->SetNeedsListening(true);
            }

            mbUpdated = true;

            fillUndoDoc(aOldPos, rGroup.mnLength, *pOldCode);
        }

        if (aRes.mbValueChanged)
        {
            for (pp = rGroup.mpCells; pp != ppEnd; ++pp)
            {
                ScFormulaCell* p = *pp;
                p->SetNeedsDirty(true);
            }
        }
    }

    void updateRefOnMove( FormulaGroup& rGroup )
    {
        if (!rGroup.mbShared)
@@ -2484,21 +2546,26 @@ class UpdateRefOnNonCopy : std::unary_function<FormulaGroup, void>
                p->SetDirty();
            }

            if (mpUndoDoc)
            {
                // Insert the old formula group into the undo document.
                ScAddress aUndoPos = aOldPos;
                ScFormulaCell* pFC = new ScFormulaCell(mpUndoDoc, aUndoPos, pOldCode->Clone());
                ScFormulaCellGroupRef xGroup = pFC->CreateCellGroup(rGroup.mnLength, false);
            fillUndoDoc(aOldPos, rGroup.mnLength, *pOldCode);
        }
    }

                mpUndoDoc->SetFormulaCell(aUndoPos, pFC);
                aUndoPos.IncRow();
                for (size_t i = 1; i < rGroup.mnLength; ++i, aUndoPos.IncRow())
                {
                    pFC = new ScFormulaCell(mpUndoDoc, aUndoPos, xGroup);
                    mpUndoDoc->SetFormulaCell(aUndoPos, pFC);
                }
            }
    void fillUndoDoc( const ScAddress& rOldPos, SCROW nLength, const ScTokenArray& rOldCode )
    {
        if (!mpUndoDoc)
            return;

        // Insert the old formula group into the undo document.
        ScAddress aUndoPos = rOldPos;
        ScFormulaCell* pFC = new ScFormulaCell(mpUndoDoc, aUndoPos, rOldCode.Clone());
        ScFormulaCellGroupRef xGroup = pFC->CreateCellGroup(nLength, false);

        mpUndoDoc->SetFormulaCell(aUndoPos, pFC);
        aUndoPos.IncRow();
        for (SCROW i = 1; i < nLength; ++i, aUndoPos.IncRow())
        {
            pFC = new ScFormulaCell(mpUndoDoc, aUndoPos, xGroup);
            mpUndoDoc->SetFormulaCell(aUndoPos, pFC);
        }
    }

@@ -2511,10 +2578,16 @@ public:

    void operator() ( FormulaGroup& rGroup )
    {
        if (mpCxt->meMode == URM_MOVE)
        switch (mpCxt->meMode)
        {
            updateRefOnMove(rGroup);
            return;
            case URM_INSDEL:
                updateRefOnShift(rGroup);
                return;
            case URM_MOVE:
                updateRefOnMove(rGroup);
                return;
            default:
                ;
        }

        if (rGroup.mbShared)
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index a872e91..d0ebd2f 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -957,6 +957,12 @@ bool ScFormulaCell::GetDirty() const { return bDirty; }
void ScFormulaCell::ResetDirty() { bDirty = bTableOpDirty = mbPostponedDirty = false; }
bool ScFormulaCell::NeedsListening() const { return bNeedListening; }
void ScFormulaCell::SetNeedsListening( bool bVar ) { bNeedListening = bVar; }

void ScFormulaCell::SetNeedsDirty( bool bVar )
{
    mbPostponedDirty = bVar;
}

void ScFormulaCell::SetNeedNumberFormat( bool bVal ) { mbNeedsNumberFormat = bVal; }
short ScFormulaCell::GetFormatType() const { return nFormatType; }

diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index afb8f5f..68f2ba5 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -769,13 +769,13 @@ struct ConventionOOO_A1 : public Convention_A1
            rBuffer.append('.');
        if (!rRef.IsColRel())
            rBuffer.append('$');
        if (!ValidCol(rAbsRef.Col()))
        if (!ValidCol(rAbsRef.Col()) || rRef.IsColDeleted())
            rBuffer.append(rErrRef);
        else
            MakeColStr(rBuffer, rAbsRef.Col());
        if (!rRef.IsRowRel())
            rBuffer.append('$');
        if (!ValidRow(rAbsRef.Row()))
        if (!ValidRow(rAbsRef.Row()) || rRef.IsRowDeleted())
            rBuffer.append(rErrRef);
        else
            MakeRowStr(rBuffer, rAbsRef.Row());
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 7052049..c7c8e42 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -2480,6 +2480,25 @@ void setRefDeleted( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt )
        rRef.SetTabDeleted(true);
}

void restoreDeletedRef( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt )
{
    if (rCxt.mnColDelta)
    {
        if (rRef.IsColDeleted())
            rRef.SetColDeleted(false);
    }
    else if (rCxt.mnRowDelta)
    {
        if (rRef.IsRowDeleted())
            rRef.SetRowDeleted(false);
    }
    else if (rCxt.mnTabDelta)
    {
        if (rRef.IsTabDeleted())
            rRef.SetTabDeleted(false);
    }
}

void setRefDeleted( ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt )
{
    if (rCxt.mnColDelta < 0)
@@ -2647,6 +2666,18 @@ sc::RefUpdateResult ScTokenArray::AdjustReferenceOnShift( const sc::RefUpdateCon
                    break;
                }

                if (!rCxt.isDeleted() && rRef.IsDeleted())
                {
                    // Check if the token has reference to previously deleted region.
                    ScAddress aCheckPos = rRef.toAbs(aNewPos);
                    if (rCxt.maRange.In(aCheckPos))
                    {
                        restoreDeletedRef(rRef, rCxt);
                        aRes.mbValueChanged = true;
                        break;
                    }
                }

                if (rCxt.maRange.In(aAbs))
                {
                    aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);